﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using Numbers.Transactions;

namespace Numbers.Arithmatic
{
    public class Division
    {
        /// <summary>
        /// Result object
        /// </summary>
        private Operand _result;

        /// <summary>
        /// Numerator object
        /// </summary>
        private Operand _numerator;

        /// <summary>
        /// Denominator object
        /// </summary>
        private Operand _denominator;

        /// <summary>
        /// Internal ctor for creating the division linkage. Really, once created, one does not hold onto it
        /// It just hangs out in memory kept safe from the garbage collector by virtue of the delegates we have
        /// setup.
        /// </summary>
        /// <param name="result"></param>
        /// <param name="numerator"></param>
        /// <param name="denominator"></param>
        private Division(Operand result, Operand numerator, Operand denominator)
        {
            ///
            /// Initial setup of the operation
            ///

            _result = result;
            _numerator = numerator;
            _denominator = denominator;
            
            if (result.isFixed)
            {
                throw new ArgumentException("Result of division cannot be fixed!");
            }

            ///
            /// Next, make sure we can setup with our current inputs
            /// 

            double r = CalculateResult();
            result.v.Val = r;
            if (result.v.Val != r)
            {
                throw new ArgumentException("Unable to make the result correct!");
            }

            ///
            /// Watch them
            /// 

            result.v.ProposedPropertyChange += new Transactions.ProposedPropertyChangeCallback(result_ProposedPropertyChange);
            numerator.v.ProposedPropertyChange += new Transactions.ProposedPropertyChangeCallback(v_ProposedPropertyChange);
            denominator.v.ProposedPropertyChange += new Transactions.ProposedPropertyChangeCallback(v_ProposedPropertyChange);

            ///
            /// Make sure we are all part of one transaction
            /// 

            List<Value> allValues = new List<Value>();
            allValues.Add(result.v);
            allValues.Add(numerator.v);
            allValues.Add(denominator.v);

            Manager.ManageByOneManager(allValues.ToArray());

        }

        /// <summary>
        /// One of the operatnds has changed. See if we can make it stick!
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="propName"></param>
        /// <returns></returns>
        bool v_ProposedPropertyChange(Transactions.IObjectInTransaction obj, string propName)
        {
            _result.v.Val = CalculateResult();
            return true;
        }

        /// <summary>
        /// The result has changed. Actually, we dan't deal with this!
        /// Hopefully there are no rounding errors! :-)
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="propName"></param>
        /// <returns></returns>
        bool result_ProposedPropertyChange(Transactions.IObjectInTransaction obj, string propName)
        {
            return _result.v.Val == CalculateResult();
        }

        /// <summary>
        /// Figure out what the result really is.
        /// </summary>
        /// <returns></returns>
        private double CalculateResult()
        {
            if (_denominator.v.Val != 0)
            {
                return _numerator.v.Val / _denominator.v.Val;
            }
            else
            {
                return 0.0;
            }
        }

        /// <summary>
        /// Creates a division of two numbers.
        /// </summary>
        /// <param name="multInfo"></param>
        /// <param name="multInfo_2"></param>
        /// <param name="multInfo_3"></param>
        public static void Create(Operand result, Operand numerator, Operand denominator)
        {
            new Division(result, numerator, denominator);
        }
    }
}
